#pragma once

#include "../common/Log.hpp"
#include "../common/SqliteHelper.hpp"
#include "../common/FileHelper.hpp"
#include "../common/message.pb.h"
#include "Exchange.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <cassert>

namespace micromq
{
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string& dbFileName): _sqliteHelper(dbFileName)
        {
            std::string parentPath = FileHelper::getParentPath(dbFileName);
            FileHelper::createDir(parentPath);
            assert(_sqliteHelper.open());
            createTable();
        }

        void createTable()
        {
            std::stringstream sql;
            sql << "create table if not exists exchange (          ";
            sql << "    `name`         varchar(128) primary key,   ";
            sql << "    `type`         int,                        ";
            sql << "    `durable`      int,                        ";
            sql << "    `auto_delete`  int,                        ";
            sql << "    `args`         varchar(128)                ";
            sql << ");                                             ";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("create table exchange error, abort...");
                ::abort();
            }
        }

        void removeTable()
        {
            const std::string sql = "drop table if exists exchange;";
            if(!_sqliteHelper.execute(sql, nullptr, nullptr))
            {
                logError("drop table exchange error...");
            }
        }

        void insertExchange(Exchange::ptr& exchange)
        {
            std::stringstream sql;
            sql << "insert into exchange values ( '";
            sql << exchange->_name << "', ";
            sql << exchange->_type << ", ";
            sql << exchange->_durable << ", ";
            sql << exchange->_autoDelete << ", '";
            sql << exchange->getArgs() << "'";
            sql << ");";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("insert into exchang error...");
            }
        }

        void deleteExchange(const std::string& name)
        {
            std::string sql = "delete from exchange where name='" + name + "';";
            if(!_sqliteHelper.execute(sql, nullptr, nullptr))
            {
                logError("delete exchang: %s error...", name.c_str());
            }
        }

        std::unordered_map<std::string, Exchange::ptr> selectAll()
        {
            std::string sql = "select * from exchange;";
            std::unordered_map<std::string, Exchange::ptr> result;
            if(!_sqliteHelper.execute(sql, onSelectAll, &result))
            {
                logError("select all from exchange error...");
            }
            return result;
        }

        ~ExchangeMapper()
        {}
    private:
        static int onSelectAll(void* args, int colNum, char** result, char** fieldName)
        {
            auto exchangeMap = static_cast<std::unordered_map<std::string, Exchange::ptr>*>(args);
            auto exchange = std::make_shared<Exchange>();
            exchange->_name = result[0];
            exchange->_type = static_cast<ExchangeType>(std::stoi(result[1]));
            exchange->_durable = static_cast<bool>(std::stoi(result[2]));
            exchange->_autoDelete = static_cast<bool>(std::stoi(result[3]));
            if(result[4] != nullptr) exchange->setArgs(result[4]);
            exchangeMap->insert(std::make_pair(exchange->_name, exchange));
            return 0;
        }
    private:
        SqliteHelper _sqliteHelper;
    };
}